package co.fitstart.weapp.api.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo2do.core.entity.exception.BusinessException;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.annotation.Dao;

import co.fitstart.entity.Constant;
import co.fitstart.entity.course.CourseType;
import co.fitstart.entity.course.ResultReport;
import co.fitstart.entity.course.round.CourseRound;
import co.fitstart.entity.course.round.Goal;
import co.fitstart.entity.course.round.RoundCoursewareStatus;
import co.fitstart.entity.course.round.RoundStatus;
import co.fitstart.entity.course.schedule.CourseSchedule;
import co.fitstart.entity.course.schedule.ScheduleStatus;
import co.fitstart.entity.courseware.CoursewareTag;
import co.fitstart.entity.courseware.FitnessTag;
import co.fitstart.entity.courseware.assessment.BasicData;
import co.fitstart.entity.courseware.assessment.PostureJoint;
import co.fitstart.entity.courseware.assessment.StudentAssessment;
import co.fitstart.entity.courseware.assessment.StudentHabit;
import co.fitstart.entity.courseware.dietary.Dietary;
import co.fitstart.entity.courseware.dietary.DietaryProgram;
import co.fitstart.entity.courseware.dietary.DietaryProgramTemplate;
import co.fitstart.entity.courseware.dietary.DietaryProgramType;
import co.fitstart.entity.courseware.exercise.Exercise;
import co.fitstart.entity.courseware.exercise.ExerciseContent;
import co.fitstart.entity.courseware.exercise.PlannedSchedule;
import co.fitstart.entity.courseware.fitnessgroup.BatchCourseware;
import co.fitstart.entity.courseware.fitnessgroup.BatchCoursewareTemplate;
import co.fitstart.entity.courseware.fitnessgroup.BatchCoursewareType;
import co.fitstart.entity.courseware.fitnessgroup.Courseware;
import co.fitstart.entity.courseware.fitnessgroup.CoursewareEditStatus;
import co.fitstart.entity.courseware.fitnessgroup.CoursewareMovementType;
import co.fitstart.entity.courseware.fitnessgroup.CoursewareTemplate;
import co.fitstart.entity.courseware.fitnessgroup.DailyCourseware;
import co.fitstart.entity.courseware.fitnessgroup.FitnessGroup;
import co.fitstart.entity.courseware.fitnessgroup.SpecialCoursewareTemplate;
import co.fitstart.entity.courseware.movement.Movement;
import co.fitstart.entity.principal.Principal;
import co.fitstart.entity.user.Student;
import co.fitstart.entity.user.basic.BodyDataTarget;
import co.fitstart.entity.utils.DateFormatUtils;
import co.fitstart.weapp.api.dao.CourseRoundDao;
import co.fitstart.weapp.api.dao.CourseScheduleDao;
import co.fitstart.weapp.api.dao.CoursewareDao;
import co.fitstart.weapp.api.dao.CoursewareTemplateDao;
import co.fitstart.weapp.api.dao.FitnessGroupDao;
import co.fitstart.weapp.api.entity.CoursewareRequest;
import co.fitstart.weapp.api.entity.DietaryRequest;
import co.fitstart.weapp.api.entity.ExerciseRequest;
import co.fitstart.weapp.api.handler.SmsSender;
import co.fitstart.weapp.api.service.CoursewareService;

/**
 * 
 * @author haiyan_xiao
 *
 */
@Service("coursewareService")
public class CoursewareServiceImpl implements CoursewareService {

    @Dao
    private CoursewareDao coursewareDao;

    @Autowired
    private GenericDaoSupport genericDaoSupport;

    @Dao
    private CourseScheduleDao courseScheduleDao;

    @Dao
    private CourseRoundDao courseRoundDao;

    @Dao
    private CoursewareTemplateDao coursewareTemplateDao;

    @Dao
    private FitnessGroupDao fitnessGroupDao;

    @Autowired
    private SmsSender smsSender;

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#getSession(co.fitstart.entity.course.schedule.CourseSchedule)
     */
    public int getSession(CourseSchedule courseSchedule) {
        return coursewareDao.count(courseSchedule.getCourseRound(), Arrays.asList(new ScheduleStatus[]{ScheduleStatus.READY, ScheduleStatus.FINISH, ScheduleStatus.EXPIRY}), courseSchedule.getOccurDate(), courseSchedule.getOccurHour());
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#getPlannedSchedule(co.fitstart.entity.course.round.CourseRound, int)
     */
    public PlannedSchedule getPlannedSchedule(CourseRound courseRound, int session) {
        List<PlannedSchedule> plannedSchedules = coursewareDao.listPlannedSchedule(courseRound, session - 1, 1);
        
        return plannedSchedules.isEmpty() ? null : plannedSchedules.get(0);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listCourseWares(co.fitstart.entity.course.schedule.CourseSchedule)
     */
    public List<Courseware> listCourseware(CourseSchedule courseSchedule) {
        return coursewareDao.listCoursewre(courseSchedule);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listGeneratedCourseware(co.fitstart.entity.course.schedule.CourseSchedule)
     */
    public List<Courseware> listGeneratedCourseware(CourseSchedule courseSchedule) {
        return coursewareDao.listGeneratedCoursewre(courseSchedule);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listWeeklyPlan(co.fitstart.entity.course.round.CourseRound)
     */
    public List<BatchCourseware> listWeeklyPlan(CourseRound courseRound) {
        return coursewareDao.listBatchCourseware(courseRound, BatchCoursewareType.WEEKLY, CoursewareEditStatus.GENERATED);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#generallyListWeeklyPlan(co.fitstart.entity.course.round.CourseRound)
     */
    public List<BatchCourseware> generallyListWeeklyPlan(CourseRound courseRound) {
        this.initBatchCourseware(courseRound);
        
        return coursewareDao.listBatchCourseware(courseRound, BatchCoursewareType.WEEKLY, CoursewareEditStatus.GENERATED);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listAllWeeklyPlan(co.fitstart.entity.course.round.CourseRound)
     */
    public List<BatchCourseware> listAllWeeklyPlan(CourseRound courseRound) {
        return coursewareDao.listBatchCourseware(courseRound, BatchCoursewareType.WEEKLY);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#generallyListAllWeeklyPlan(co.fitstart.entity.course.round.CourseRound)
     */
    public List<BatchCourseware> generallyListAllWeeklyPlan(CourseRound courseRound) {
        this.initBatchCourseware(courseRound);
        
        return coursewareDao.listBatchCourseware(courseRound, BatchCoursewareType.WEEKLY);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadBatchCourseware(java.lang.Long)
     */
    public BatchCourseware loadBatchCourseware(Long batchCoursewareId) {
        BatchCourseware batchCourseware = genericDaoSupport.load(BatchCourseware.class, batchCoursewareId);
        return batchCourseware != null && !batchCourseware.isDisabled() ? batchCourseware : null;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listDietary(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware)
     */
    public Map<String, DietaryProgram> listDietary(BatchCourseware batchCourseware) {
        Map<String, DietaryProgram> dietarys = new HashMap<>();
        
        if(batchCourseware.getDietaryPrograms() != null && !batchCourseware.getDietaryPrograms().isEmpty()) {
            DietaryProgram breakfast = batchCourseware.getDietaryPrograms().get(DietaryProgramType.BREAKFAST);
            if(breakfast != null) {
                dietarys.put("breakfast", breakfast);
                
                if(breakfast.getSnacks() != null) {
                    dietarys.put("breakfastSnack", genericDaoSupport.load(DietaryProgram.class, breakfast.getSnacks()));
                }
            }
            
            DietaryProgram lunch = batchCourseware.getDietaryPrograms().get(DietaryProgramType.LUNCH);
            if(lunch != null) {
                dietarys.put("lunch", lunch);
                
                if(lunch.getSnacks() != null) {
                    dietarys.put("lunchSnack", genericDaoSupport.load(DietaryProgram.class, lunch.getSnacks()));
                }
            }
            
            DietaryProgram dinner = batchCourseware.getDietaryPrograms().get(DietaryProgramType.DINNER);
            if(dinner != null) {
                dietarys.put("dinner", dinner);
                
                if(breakfast.getSnacks() != null) {
                    dietarys.put("dinnerSnack", genericDaoSupport.load(DietaryProgram.class, dinner.getSnacks()));
                }
            }
        }
        
        return dietarys;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listCoursewareTags(java.util.List)
     */
    public List<CoursewareTag> listCoursewareTags(List<Long> tagIds) {
        if(!tagIds.isEmpty()) {
            return coursewareDao.listCoursewareTag(tagIds);
        } else {
            return null;
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadDailyCourseware(java.lang.Long)
     */
    public DailyCourseware loadDailyCourseware(Long dailyCoursewareId) {
        DailyCourseware dailyCourseware = genericDaoSupport.load(DailyCourseware.class, dailyCoursewareId);
        return dailyCourseware != null && !dailyCourseware.isDisabled() ? dailyCourseware : null;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listCourseware(co.fitstart.entity.courseware.fitnessgroup.DailyCourseware, co.fitstart.entity.courseware.fitnessgroup.CoursewareMovementType)
     */
    public List<Courseware> listCourseware(DailyCourseware dailyCourseware, CoursewareMovementType type) {
        return coursewareDao.listCourseware(dailyCourseware, type);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadCourseware(java.lang.Long)
     */
    public Courseware loadCourseware(Long coursewareId) {
        Courseware courseware = genericDaoSupport.load(Courseware.class, coursewareId);
        return courseware != null && !courseware.isDisabled() ? courseware : null;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadGoal(co.fitstart.entity.course.round.CourseRound)
     */
    public Goal loadGoal(CourseRound courseRound) {
        return coursewareDao.loadGoal(courseRound);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listBodyDataTarget(co.fitstart.entity.course.round.CourseRound)
     */
    public List<BodyDataTarget> listBodyDataTarget(CourseRound courseRound) {
        return coursewareDao.listBodyDataTarget(courseRound);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listStudentAssessment(co.fitstart.entity.course.round.CourseRound)
     */
    public List<StudentAssessment> listStudentAssessment(CourseRound courseRound) {
        return coursewareDao.listStudentAssessment(courseRound);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadStudentAssessment(java.lang.Long)
     */
    public StudentAssessment loadStudentAssessment(Long studentAssessmentId) {
        return genericDaoSupport.load(StudentAssessment.class, studentAssessmentId);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadStudentAssessment(co.fitstart.entity.course.round.CourseRound)
     */
    public StudentAssessment loadStudentAssessment(CourseRound courseRound) {
        return coursewareDao.loadStudentAssessment(courseRound);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadBasicData(co.fitstart.entity.course.round.CourseRound)
     */
    public BasicData loadBasicData(CourseRound courseRound) {
        return coursewareDao.loadBasicData(courseRound);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadStudentHabit(co.fitstart.entity.course.round.CourseRound)
     */
    public StudentHabit loadStudentHabit(CourseRound courseRound) {
        return coursewareDao.loadStudentHabit(courseRound);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadPostureJoint(co.fitstart.entity.course.round.CourseRound)
     */
    public PostureJoint loadPostureJoint(CourseRound courseRound) {
        return coursewareDao.loadPostureJoint(courseRound);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#generallyLoadDietary(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware)
     */
    public Dietary generallyLoadDietary(BatchCourseware batchCourseware) {
        Dietary dietary = this.loadDietary(batchCourseware);
        
        if(dietary != null && (StringUtils.isNotBlank(dietary.getBreakfastDescription()) || StringUtils.isNotBlank(dietary.getBreakfastSnackDescription())
                || StringUtils.isNotBlank(dietary.getLunchDescription()) || StringUtils.isNotBlank(dietary.getLunchSnackDescription())
                || StringUtils.isNotBlank(dietary.getDinnerDescription()) || StringUtils.isNotBlank(dietary.getDinnerSnackDescription())
                || StringUtils.isNotBlank(dietary.getWaterIntake()) || StringUtils.isNotBlank(dietary.getRemark()))) {
            return dietary;
        } else if(batchCourseware.getDietaryPrograms() != null && !batchCourseware.getDietaryPrograms().isEmpty()) {
            Map<String, DietaryProgram> dietaryPrograms = batchCourseware.getDietaryPrograms();
            
            DietaryProgramTemplate breakfastTemplate = null;
            String breakfastDescription = null;
            DietaryProgramTemplate breakfastSnackTemplate = null;
            String breakfastSnackDescription = null;
            if(dietaryPrograms.get(DietaryProgramType.BREAKFAST) != null) {
                breakfastTemplate = dietaryPrograms.get(DietaryProgramType.BREAKFAST).getTemplate();
                breakfastDescription = dietaryPrograms.get(DietaryProgramType.BREAKFAST).getDescription();
                
                if(dietaryPrograms.get(DietaryProgramType.BREAKFAST).getSnacks() != null) {
                    DietaryProgram breakfastSnack = genericDaoSupport.load(DietaryProgram.class, dietaryPrograms.get(DietaryProgramType.BREAKFAST).getSnacks());
                    
                    if(breakfastSnack != null) {
                        breakfastSnackTemplate = breakfastSnack.getTemplate();
                        breakfastSnackDescription = breakfastSnack.getDescription();
                    }
                }
            }
            
            DietaryProgramTemplate lunchTemplate = null;
            String lunchDescription = null;
            DietaryProgramTemplate lunchSnackTemplate = null;
            String lunchSnackDescription = null;
            if(dietaryPrograms.get(DietaryProgramType.LUNCH) != null) {
                lunchTemplate = dietaryPrograms.get(DietaryProgramType.LUNCH).getTemplate();
                lunchDescription = dietaryPrograms.get(DietaryProgramType.LUNCH).getDescription();
                
                if(dietaryPrograms.get(DietaryProgramType.LUNCH).getSnacks() != null) {
                    DietaryProgram lunchSnack = genericDaoSupport.load(DietaryProgram.class, dietaryPrograms.get(DietaryProgramType.LUNCH).getSnacks());
                    
                    if(lunchSnack != null) {
                        lunchSnackTemplate = lunchSnack.getTemplate();
                        lunchSnackDescription = lunchSnack.getDescription();
                    }
                }
            }
            
            DietaryProgramTemplate dinnerTemplate = null;
            String dinnerDescription = null;
            DietaryProgramTemplate dinnerSnackTemplate = null;
            String dinnerSnackDescription = null;
            if(dietaryPrograms.get(DietaryProgramType.DINNER) != null) {
                dinnerTemplate = dietaryPrograms.get(DietaryProgramType.DINNER).getTemplate();
                dinnerDescription = dietaryPrograms.get(DietaryProgramType.DINNER).getDescription();
                
                if(dietaryPrograms.get(DietaryProgramType.DINNER).getSnacks() != null) {
                    DietaryProgram dinnerSnack = genericDaoSupport.load(DietaryProgram.class, dietaryPrograms.get(DietaryProgramType.DINNER).getSnacks());
                    
                    if(dinnerSnack != null) {
                        dinnerSnackTemplate = dinnerSnack.getTemplate();
                        dinnerSnackDescription = dinnerSnack.getDescription();
                    }
                }
            }
            
            if(dietary == null) {
                dietary = new Dietary(batchCourseware);
                genericDaoSupport.save(dietary.edit(breakfastTemplate, breakfastDescription, breakfastSnackTemplate, breakfastSnackDescription,
                        lunchTemplate, lunchDescription, lunchSnackTemplate, lunchSnackDescription,
                        dinnerTemplate, dinnerDescription, dinnerSnackTemplate, dinnerSnackDescription, null, null));
            } else {
                genericDaoSupport.update(dietary.edit(breakfastTemplate, breakfastDescription, breakfastSnackTemplate, breakfastSnackDescription,
                        lunchTemplate, lunchDescription, lunchSnackTemplate, lunchSnackDescription,
                        dinnerTemplate, dinnerDescription, dinnerSnackTemplate, dinnerSnackDescription, null, null));
            }
            
            return dietary;
        } else {
            BatchCoursewareTemplate batchCoursewareTemplate = coursewareTemplateDao.loadBatchCoursewareTemplate(batchCourseware.getCourseRound().getFitnessGroup(), BatchCoursewareType.WEEKLY, batchCourseware.getSorting()%6 == 0 ? 6 : batchCourseware.getSorting()%6);
            
            if(batchCoursewareTemplate != null) {
                DietaryProgramTemplate breakfastTemplate = batchCoursewareTemplate.getBreakfast();
                String breakfastDescription = null;
                DietaryProgramTemplate breakfastSnackTemplate = null;
                String breakfastSnackDescription = null;
                if(breakfastTemplate != null) {
                    breakfastDescription = breakfastTemplate.getDescription();
                    
                    if(breakfastTemplate.getSnacks() != null) {
                        breakfastSnackTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, breakfastTemplate.getSnacks());
                        
                        if(breakfastSnackTemplate != null) {
                            breakfastSnackDescription = breakfastSnackTemplate.getDescription();
                        }
                    }
                }
                
                DietaryProgramTemplate lunchTemplate = batchCoursewareTemplate.getLunch();
                String lunchDescription = null;
                DietaryProgramTemplate lunchSnackTemplate = null;
                String lunchSnackDescription = null;
                if(lunchTemplate != null) {
                    lunchDescription = lunchTemplate.getDescription();
                    
                    if(lunchTemplate.getSnacks() != null) {
                        lunchSnackTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, lunchTemplate.getSnacks());
                        
                        if(lunchSnackTemplate != null) {
                            lunchSnackDescription = lunchSnackTemplate.getDescription();
                        }
                    }
                }
                
                DietaryProgramTemplate dinnerTemplate = batchCoursewareTemplate.getDinner();
                String dinnerDescription = null;
                DietaryProgramTemplate dinnerSnackTemplate = null;
                String dinnerSnackDescription = null;
                if(dinnerTemplate != null) {
                    dinnerDescription = dinnerTemplate.getDescription();
                    
                    if(dinnerTemplate.getSnacks() != null) {
                        dinnerSnackTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dinnerTemplate.getSnacks());
                        
                        if(dinnerSnackTemplate != null) {
                            dinnerSnackDescription = dinnerSnackTemplate.getDescription();
                        }
                    }
                }
                
                if(dietary == null) {
                    dietary = new Dietary(batchCourseware);
                    genericDaoSupport.save(dietary.edit(breakfastTemplate, breakfastDescription, breakfastSnackTemplate, breakfastSnackDescription,
                            lunchTemplate, lunchDescription, lunchSnackTemplate, lunchSnackDescription,
                            dinnerTemplate, dinnerDescription, dinnerSnackTemplate, dinnerSnackDescription, null, null));
                } else {
                    genericDaoSupport.update(dietary.edit(breakfastTemplate, breakfastDescription, breakfastSnackTemplate, breakfastSnackDescription,
                            lunchTemplate, lunchDescription, lunchSnackTemplate, lunchSnackDescription,
                            dinnerTemplate, dinnerDescription, dinnerSnackTemplate, dinnerSnackDescription, null, null));
                }
                
                return dietary;
            } else {
                return null;
            }
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadDietary(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware)
     */
    public Dietary loadDietary(BatchCourseware batchCourseware) {
        return coursewareDao.loadDietary(batchCourseware);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadBatchCourseware(co.fitstart.entity.course.round.CourseRound, int)
     */
    public BatchCourseware loadBatchCourseware(CourseRound courseRound, int week) {
        return coursewareDao.loadBatchCourseare(courseRound, week);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadExercise(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware)
     */
    public Exercise loadExercise(BatchCourseware batchCourseware) {
        return coursewareDao.loadExercise(batchCourseware);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadResultReport(co.fitstart.entity.course.round.CourseRound)
     */
    public ResultReport loadResultReport(CourseRound courseRound) {
        return coursewareDao.loadResultReport(courseRound);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#editResultReportSummary(co.fitstart.entity.course.round.CourseRound, java.lang.String, java.lang.String)
     */
    public void editResultReportSummary(CourseRound courseRound, String summary, String wordsToCoach) {
        ResultReport resultReport = coursewareDao.loadResultReport(courseRound);
        
        if(resultReport == null) {
            resultReport = new ResultReport(courseRound);
            genericDaoSupport.save(resultReport.editStudentSummary(summary, wordsToCoach));
        } else {
            genericDaoSupport.update(resultReport.editStudentSummary(summary, wordsToCoach));
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadBatchCourseware(co.fitstart.entity.course.round.CourseRound)
     */
    public BatchCourseware loadBatchCourseware(CourseRound courseRound) {
        return coursewareDao.loadBatchCourseware(courseRound, BatchCoursewareType.COURSE);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listCourseware(co.fitstart.entity.courseware.fitnessgroup.DailyCourseware)
     */
    public List<Courseware> listCourseware(DailyCourseware dailyCourseware) {
        return coursewareDao.listCourseware(dailyCourseware);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listPlannedSchedule(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware)
     */
    public List<Map<String, Object>> listPlannedSchedule(BatchCourseware batchCourseware) {
        List<Map<String, Object>> result = new ArrayList<>();
        
        List<PlannedSchedule> plannedSchedules = coursewareDao.listPlannedSchedule(batchCourseware);
        
        if(!plannedSchedules.isEmpty()) {
            int begin = 0;
            if(batchCourseware.getSorting() > 1) {
                begin = coursewareDao.countPlannedSchedule(batchCourseware.getCourseRound(), batchCourseware.getSorting());
            }
            
            List<CourseSchedule> courseSchedules = courseScheduleDao.list(batchCourseware.getCourseRound(), Arrays.asList(new ScheduleStatus[]{ScheduleStatus.READY, ScheduleStatus.EXPIRY, ScheduleStatus.FINISH}), begin, plannedSchedules.size());
            
            int i = 0;
            for(PlannedSchedule plannedSchedule : plannedSchedules) {
                Map<String, Object> map = new HashMap<>();
                
                map.put("day", plannedSchedule.getDay());
                map.put("content", plannedSchedule.getContentString());
                
                CourseSchedule courseSchedule = courseSchedules.size() > i ? courseSchedules.get(i++) : null;
                map.put("coach", courseSchedule == null ? null : courseSchedule.getCoachName());
                map.put("course_schedule_id", courseSchedule == null ? null : courseSchedule.getId());
                
                result.add(map);
            }
        }
        
        return result;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#getMaxFinishedCourseCourseware(co.fitstart.entity.course.round.CourseRound)
     */
    public int getMaxFinishedCourseCourseware(CourseRound courseRound) {
        //if(courseRound.getCourseType().isIndividual()) {
            CourseSchedule courseSchedule = coursewareDao.getLatestCourseCourseware(courseRound);
            
            if(courseSchedule != null) {
                return courseScheduleDao.count(courseRound, courseSchedule.getOccurDate(), courseSchedule.getOccurHour());
            } else {
                return 0;
            }
//        } else if(courseRound.getCourseType().isYear()) {
//            Integer plannedCourse = coursewareDao.getMaxSortingOfCourseCourseware(courseRound, CoursewareEditStatus.GENERATED, BatchCoursewareType.COURSE);
//            
//            return plannedCourse == null ? 0 : plannedCourse;
//        } else {
//            return 0;
//        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#getMaxFinishedWeeklyPlan(co.fitstart.entity.course.round.CourseRound)
     */
    public int getMaxFinishedWeeklyPlan(CourseRound courseRound) {
        Integer plannedWeekly = coursewareDao.getMaxSortingOfWeeklyPlan(courseRound, CoursewareEditStatus.GENERATED, BatchCoursewareType.WEEKLY);
        
        return plannedWeekly == null ? 0 : plannedWeekly;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listGrouping(co.fitstart.entity.principal.Principal)
     */
    public List<CourseRound> listGrouping(Principal coach) {
        List<RoundStatus> roundStatuss = Arrays.asList(new RoundStatus[]{RoundStatus.PREPARE, RoundStatus.READY, RoundStatus.HOLD, RoundStatus.EXPIRE, RoundStatus.ACTIVATE});
        List<CourseType> courseTypes = Arrays.asList(new CourseType[]{CourseType.INDIVIDUAL, CourseType.YEAR});
        
        return courseRoundDao.list(coach, RoundCoursewareStatus.GROUPING, roundStatuss, courseTypes);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listCourseRound(co.fitstart.entity.principal.Principal, java.lang.String)
     */
    public List<Map<String, Object>> listCourseRound(Principal coach, String status) {
        List<Map<String, Object>> result = new ArrayList<>();
        
        //List<RoundStatus> roundStatuss = Arrays.asList(new RoundStatus[]{RoundStatus.PREPARE, RoundStatus.READY, RoundStatus.HOLD, RoundStatus.EXPIRE, RoundStatus.ACTIVATE});
        List<CourseType> courseTypes = Arrays.asList(new CourseType[]{CourseType.INDIVIDUAL, CourseType.YEAR});
        
//        if("editing".equals(status)) {
//            result = listPlannedCourse(courseRoundDao.list(coach, RoundCoursewareStatus.EDITING, roundStatuss, courseTypes));
//        } else if("finish".equals(status)) {
//            result = listPlannedCourse(courseRoundDao.list(coach, RoundCoursewareStatus.FINISH, RoundStatus.FINISH, courseTypes));
//        } else if("cancel".equals(status)) {
//            result = listPlannedCourse(courseRoundDao.list(coach, RoundStatus.CANCEL, courseTypes));
//        }
        
        if("prepare".equals(status)) {
            result = listPlannedCourse(courseRoundDao.list(coach, RoundStatus.PREPARE, courseTypes));
        } else if("ready".equals(status)) {
            result = listPlannedCourse(courseRoundDao.list(coach, Arrays.asList(new RoundStatus[]{RoundStatus.READY, RoundStatus.HOLD, RoundStatus.EXPIRE, RoundStatus.ACTIVATE, RoundStatus.CANCEL}), courseTypes));
        } else if("finish".equals(status)) {
            result = listPlannedCourse(courseRoundDao.list(coach, RoundStatus.FINISH, courseTypes));
        }
        
        return result;
    }

    /**
     * 
     * @param courseRounds
     * @return
     */
    private List<Map<String, Object>> listPlannedCourse(List<CourseRound> courseRounds) {
        List<Map<String, Object>> result = new ArrayList<>();
        
        if(!courseRounds.isEmpty()) {
            for(CourseRound courseRound : courseRounds) {
                Map<String, Object> map = new HashMap<>();
                
                map.put("courseRound", courseRound);
                map.put("plannedCourse", courseRound.getFitnessGroup() == null ? 0 : getMaxFinishedCourseCourseware(courseRound));
                //map.put("plannedWeekly", courseRound.getFitnessGroup() == null ? 0 : getMaxFinishedWeeklyPlan(courseRound));
                
                result.add(map);
            }
        }
        
        return result;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#createStudentAssessment(co.fitstart.entity.course.round.CourseRound, co.fitstart.entity.courseware.assessment.BasicData, co.fitstart.entity.courseware.assessment.PostureJoint)
     */
    public void createStudentAssessment(CourseRound courseRound, BasicData basicData, PostureJoint postureJoint) {
        boolean nullBasicData = basicData.getRestingHeartRate() == null && basicData.getMaxHeartRate() == null
                && basicData.getStandardBobi() == null && basicData.getBobi() == null
                && StringUtils.isBlank(basicData.getCardiovascular()) && basicData.getvUp() == null
                && basicData.getStaticSquat() == null && basicData.getPushUp() == null
                && basicData.getDisease() == null && basicData.getRectusAbdominisIsolation() == null;
        boolean nullPostureJoint = postureJoint.getObverseHead() == null && postureJoint.getObverseScapula() == null
                && postureJoint.getObversePelvis() == null && postureJoint.getObverseKneeJoint() == null
                && postureJoint.getReverseHead() == null && postureJoint.getReverseCervicalVertebra() == null
                && postureJoint.getReverseHipJoint() == null && postureJoint.getReverseKneeJoint() == null
                && postureJoint.getReverseFoot() == null && postureJoint.getKneeDirection() == null
                && postureJoint.getParallel() == null && postureJoint.getThoracicVertebra() == null
                && postureJoint.getLegMuscle() == null && postureJoint.getHipJoint() == null
                && postureJoint.getShoulderJoint() == null && postureJoint.getAnkleJoint() == null;
        if(nullBasicData && nullPostureJoint) {
            throw new BusinessException("新评估不能为空，请至少选择一项");
        } else {
            if(!nullBasicData) {
                // create basicData
                genericDaoSupport.save(basicData.initialize(courseRound));
            }
            if(!nullPostureJoint) {
                // create postureJoint
                genericDaoSupport.save(postureJoint.initialize(courseRound));
            }
            
            // create studentAssessment
            StudentAssessment studentAssessment = new StudentAssessment(courseRound, nullBasicData ? null : basicData, nullPostureJoint ? null : postureJoint);
            genericDaoSupport.save(studentAssessment);
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listFitnessGroup(co.fitstart.entity.course.round.CourseRound)
     */
    public List<FitnessGroup> listFitnessGroup(CourseRound courseRound) {
        List<FitnessGroup> fitnessGroups = fitnessGroupDao.list(courseRound.getCourse());
        
        if(!fitnessGroups.isEmpty() && courseRound.getFitnessGroup() != null) {
            fitnessGroups.remove(courseRound.getFitnessGroup());
        }
        
        return fitnessGroups;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listStudentTags(java.util.List)
     */
    public List<Map<String, Object>> listStudentTags(List<Long> tagIds) {
        List<Map<String, Object>> result = new ArrayList<>();
        
        List<FitnessTag> tags = coursewareTemplateDao.listFitnessTag();
        if(!tags.isEmpty()) {
            for(FitnessTag tag : tags) {
                Map<String, Object> map = new HashMap<>();
                map.put("id", tag.getId());
                map.put("name", tag.getName());
                map.put("selected", tagIds != null && tagIds.contains(tag.getId()));
                result.add(map);
            }
        }
        
        return result;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#loadFitnessGroup(java.lang.Long)
     */
    public FitnessGroup loadFitnessGroup(Long fitnessGroupId) {
        FitnessGroup fitnessGroup = genericDaoSupport.load(FitnessGroup.class, fitnessGroupId);
        return (fitnessGroup == null || fitnessGroup.isDisabled()) ? null : fitnessGroup;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#editFitnessGroup(co.fitstart.entity.course.round.CourseRound, co.fitstart.entity.courseware.fitnessgroup.FitnessGroup, java.util.List)
     */
    public void editFitnessGroup(CourseRound courseRound, FitnessGroup fitnessGroup, List<Long> tagIds) {
        if(courseRound.getFitnessGroup() == null) {
            
            // create batchCourseware
            this.createBatchCourseware(fitnessGroup, courseRound);
            
            // edit student tags
            this.editStudentTag(courseRound, tagIds);
        } else if(courseRound.getFitnessGroup() != fitnessGroup) {
            this.updataBatchCourseware(fitnessGroup, courseRound);
        }
    }

    /**
     * edit student tags
     * @param courseRound
     * @param tagIds
     */
    private void editStudentTag(CourseRound courseRound, List<Long> tagIds) {
        Student student = courseRound.getStudent();
        if(tagIds != null && tagIds.size() > 0) {
            List<FitnessTag> fitnessTags = coursewareTemplateDao.listFitnessTag(tagIds);
            if(!fitnessTags.isEmpty()) {
                List<Long> remainTagIds = new ArrayList<>();
                
                if(!courseRound.getStudent().getArrayTagIds().isEmpty()) {
                    List<CoursewareTag> originalTags = coursewareDao.listCoursewareTag(courseRound.getStudent().getArrayTagIds());
                    
                    if(!originalTags.isEmpty()) {
                        for(CoursewareTag tag : originalTags) {
                            if(fitnessTags.contains(tag.getFitnessTag())) {
                                remainTagIds.add(tag.getId());
                                fitnessTags.remove(tag.getFitnessTag());
                            }
                        }
                    }
                }
                
                StringBuffer tags = new StringBuffer();
                if(!remainTagIds.isEmpty()) {
                    for(Long tagId : remainTagIds) {
                        tags.append("," + tagId);
                    }
                }
                if(!fitnessTags.isEmpty()) {
                    for(FitnessTag fitnessTag : fitnessTags) {
                        CoursewareTag coursewareTag = new CoursewareTag(fitnessTag);
                        Long key = (Long) genericDaoSupport.save(coursewareTag);
                        tags = tags.append("," + key.toString());
                    }
                }
                
                genericDaoSupport.update(student.editTag(tags.toString()));
            } else {
                genericDaoSupport.update(student.editTag(null));
            }
        } else {
            genericDaoSupport.update(student.editTag(null));
        }
    }

    /**
     * 
     * @param fitnessGroup
     * @param courseRound
     * @param week
     */
    private void createBatchCourseware(FitnessGroup fitnessGroup, CourseRound courseRound) {
        int week = courseRound.getEffectiveWeek();
        
        courseRound.addAttribute(Constant.ONLINE_PLAN_AMOUNT, week);
        genericDaoSupport.update(courseRound.changeGroup(fitnessGroup));
        
        Map<Integer, BatchCoursewareTemplate> templateMap = new HashMap<>();
        List<BatchCoursewareTemplate> batchCoursewareTemplates = coursewareTemplateDao.listBatchCoursewareTemplate(fitnessGroup, BatchCoursewareType.WEEKLY, 6);
        
        for(BatchCoursewareTemplate template : batchCoursewareTemplates) {
            templateMap.put(template.getSorting() - 1, template);
        }
        
        Date effectiveStart = null;
        Date effectiveEnd = courseRound.getEffectiveEndDay();
        if(courseRound.getEffectiveStart() != null) {
            effectiveStart = com.demo2do.core.utils.DateUtils.parseDate(DateFormatUtils.format(courseRound.getEffectiveStart(), "yyyy-MM-dd"), "yyyy-MM-dd");
        }
        
        for(int i=0; i<week; i++) {
            Date begin = null;
            Date end = null;
            if(effectiveStart != null) {
                begin = DateUtils.addWeeks(effectiveStart, i);
                end = DateUtils.addDays(begin, 6);
            }
            if(effectiveEnd != null && end.after(effectiveEnd)) {
                end = effectiveEnd;
            }
            BatchCourseware batchCourseware = new BatchCourseware(courseRound, i + 1, begin, end);
            genericDaoSupport.save(batchCourseware);
            
            // create dietary
            this.createDietary(templateMap.get(i%6), batchCourseware);
        }
        
    }

    /**
     * 
     * @param batchCoursewareTemplate
     * @param batchCourseware
     */
    private void createDietary(BatchCoursewareTemplate batchCoursewareTemplate, BatchCourseware batchCourseware) {
        if(batchCoursewareTemplate != null) {
            DietaryProgramTemplate breakfast = batchCoursewareTemplate.getBreakfast();
            DietaryProgramTemplate breakfastSnack = null;
            DietaryProgramTemplate lunch = batchCoursewareTemplate.getLunch();
            DietaryProgramTemplate lunchSnack = null;
            DietaryProgramTemplate dinner = batchCoursewareTemplate.getDinner();
            DietaryProgramTemplate dinnerSnack = null;
            
            if(breakfast != null && breakfast.getSnacks() != null) {
                breakfastSnack = genericDaoSupport.load(DietaryProgramTemplate.class, breakfast.getSnacks());
            }
            
            if(lunch != null && lunch.getSnacks() != null) {
                lunchSnack = genericDaoSupport.load(DietaryProgramTemplate.class, lunch.getSnacks());
            }
            
            if(dinner != null && dinner.getSnacks() != null) {
                dinnerSnack = genericDaoSupport.load(DietaryProgramTemplate.class, dinner.getSnacks());
            }
            
            if(breakfast != null || breakfastSnack != null || lunch != null || lunchSnack != null || dinner != null || dinnerSnack != null) {
                Dietary dietary = new Dietary(batchCourseware);
                genericDaoSupport.save(dietary.edit(breakfast, breakfast == null ? null : breakfast.getDescription(),
                        breakfastSnack, breakfastSnack == null ? null : breakfastSnack.getDescription(),
                        lunch, lunch == null ? null : lunch.getDescription(),
                        lunchSnack, lunchSnack == null ? null : lunchSnack.getDescription(),
                        dinner, dinner == null ? null : dinner.getDescription(),
                        dinnerSnack, dinnerSnack == null ? null : dinnerSnack.getDescription(), null, null));
            }
        }
    }

    /**
     * 
     * @param batchCoursewareTemplate
     * @param batchCourseware
     */
    private void updateDietary(BatchCoursewareTemplate batchCoursewareTemplate, BatchCourseware batchCourseware) {
        if(batchCoursewareTemplate != null) {
            DietaryProgramTemplate breakfast = batchCoursewareTemplate.getBreakfast();
            DietaryProgramTemplate breakfastSnack = null;
            DietaryProgramTemplate lunch = batchCoursewareTemplate.getLunch();
            DietaryProgramTemplate lunchSnack = null;
            DietaryProgramTemplate dinner = batchCoursewareTemplate.getDinner();
            DietaryProgramTemplate dinnerSnack = null;
            
            if(breakfast != null && breakfast.getSnacks() != null) {
                breakfastSnack = genericDaoSupport.load(DietaryProgramTemplate.class, breakfast.getSnacks());
            }
            
            if(lunch != null && lunch.getSnacks() != null) {
                lunchSnack = genericDaoSupport.load(DietaryProgramTemplate.class, lunch.getSnacks());
            }
            
            if(dinner != null && dinner.getSnacks() != null) {
                dinnerSnack = genericDaoSupport.load(DietaryProgramTemplate.class, dinner.getSnacks());
            }
            Dietary dietary = coursewareDao.loadDietary(batchCourseware);
            
            if((breakfast != null || breakfastSnack != null || lunch != null || lunchSnack != null || dinner != null || dinnerSnack != null) && dietary == null) {
                dietary = new Dietary(batchCourseware);
                genericDaoSupport.save(dietary.edit(breakfast, breakfast == null ? null : breakfast.getDescription(),
                        breakfastSnack, breakfastSnack == null ? null : breakfastSnack.getDescription(),
                        lunch, lunch == null ? null : lunch.getDescription(),
                        lunchSnack, lunchSnack == null ? null : lunchSnack.getDescription(),
                        dinner, dinner == null ? null : dinner.getDescription(),
                        dinnerSnack, dinnerSnack == null ? null : dinnerSnack.getDescription(), null, null));
            } else if(dietary != null) {
                genericDaoSupport.update(dietary.edit(null, null, null, null, null, null, null, null, null, null, null, null, null, null));
            }
        }
    }

    /**
     * 
     * @param fitnessGroup
     * @param courseRound
     */
    private void updataBatchCourseware(FitnessGroup fitnessGroup, CourseRound courseRound) {
        int week = courseRound.getEffectiveWeek();
        
        boolean flag = false;
        if(MapUtils.isEmpty(courseRound.getAttrs()) || courseRound.getAttrs().get(Constant.ONLINE_PLAN_AMOUNT) == null) {
            courseRound.addAttribute(Constant.ONLINE_PLAN_AMOUNT, week);
            flag = true;
        }
        
        genericDaoSupport.update(courseRound.changeGroup(fitnessGroup));
        
        // get batchCoursewareTemplate
        Map<Integer, BatchCoursewareTemplate> templateMap = this.mapBatchCoursewareTemplate(fitnessGroup);
        
        // get old batch
        List<BatchCourseware> batches = coursewareDao.listBatchCourseware(courseRound, BatchCoursewareType.WEEKLY);
        if(flag) {
            Date effectiveStart = null;
            Date effectiveEnd = courseRound.getEffectiveEndDay();
            if(courseRound.getEffectiveStart() != null) {
                effectiveStart = com.demo2do.core.utils.DateUtils.parseDate(DateFormatUtils.format(courseRound.getEffectiveStart(), "yyyy-MM-dd"), "yyyy-MM-dd");
            }
            
            for(BatchCourseware batch : batches) {
                if(effectiveStart != null) {
                    Date begin = DateUtils.addWeeks(effectiveStart, batch.getSorting() - 1);
                    Date end = DateUtils.addDays(begin, 6);
                    if(end != null && effectiveEnd != null && end.after(effectiveEnd)) {
                        end = effectiveEnd;
                    }
                    
                    genericDaoSupport.update(batch.edit(begin, end));
                }
                
                if(!batch.getEditStatus().isGenerated()) {
                    this.updateDietary(templateMap.get((batch.getSorting() - 1)%6), batch);
                }
            }
            
            if(week > courseRound.getFitnessGroupWeek()) {
                for(int i=courseRound.getFitnessGroupWeek(); i<week; i++) {
                    Date begin = null;
                    Date end = null;
                    if(effectiveStart != null) {
                        begin = DateUtils.addWeeks(effectiveStart, i);
                        end = DateUtils.addDays(begin, 6);
                    }
                    if(end != null && effectiveEnd != null && end.after(effectiveEnd)) {
                        end = effectiveEnd;
                    }
                    
                    BatchCourseware batchCourseware = new BatchCourseware(courseRound, i + 1, begin, end);
                    genericDaoSupport.save(batchCourseware);
                    
                    // create dietary
                    this.createDietary(templateMap.get(i%6), batchCourseware);
                }
            } else if(week < courseRound.getFitnessGroupWeek()) {
                int plannedWeekly = coursewareDao.countBatchCourseware(courseRound, BatchCoursewareType.WEEKLY, week, CoursewareEditStatus.GENERATED);
                if(plannedWeekly >= week) {
                    genericDaoSupport.update(courseRound.editPlannedWeekly(plannedWeekly).finishCourseware());
                } else if(plannedWeekly != courseRound.getPlannedWeekly()) {
                    genericDaoSupport.update(courseRound.editPlannedWeekly(plannedWeekly));
                }
                
                //disable batchCourseware which's sorting is bigger than week
                this.disableBatchCourseware(courseRound, week);
            }
        } else {
            for(BatchCourseware batch : batches) {
                if(!batch.getEditStatus().isGenerated()) {
                    this.updateDietary(templateMap.get((batch.getSorting() - 1)%6), batch);
                }
            }
        }
    }

    /**
     * 
     * @param courseRound
     */
    private void initBatchCourseware(CourseRound courseRound) {
        if(!courseRound.getCourseStatus().isFinish() && !courseRound.getCourseStatus().isCancel()
                && courseRound.getFitnessGroup() != null
                && (MapUtils.isEmpty(courseRound.getAttrs()) || courseRound.getAttrs().get(Constant.ONLINE_PLAN_AMOUNT) == null)) {
            int week = courseRound.getEffectiveWeek();
            
            if(courseRound.getPlannedWeekly() < week) {
                courseRound.editingCourseware();
            }
            
            genericDaoSupport.update(courseRound.addAttribute(Constant.ONLINE_PLAN_AMOUNT, week));
            
            Date effectiveStart = null;
            Date effectiveEnd = courseRound.getEffectiveEndDay();
            if(courseRound.getEffectiveStart() != null) {
                effectiveStart = com.demo2do.core.utils.DateUtils.parseDate(DateFormatUtils.format(courseRound.getEffectiveStart(), "yyyy-MM-dd"), "yyyy-MM-dd");
            }
            
            if(effectiveStart != null) {
                List<BatchCourseware> batches = coursewareDao.listBatchCourseware(courseRound, BatchCoursewareType.WEEKLY);
                for(BatchCourseware batch : batches) {
                    Date begin = null;
                    Date end = null;
                    if(effectiveStart != null) {
                        begin = DateUtils.addWeeks(effectiveStart, batch.getSorting() - 1);
                        end = DateUtils.addDays(begin, 6);
                    }
                    if(end != null && effectiveEnd != null && end.after(effectiveEnd)) {
                        end = effectiveEnd;
                    }
                    
                    genericDaoSupport.update(batch.edit(begin, end));
                }
            }
            
            if(week > courseRound.getFitnessGroupWeek()) {
                // get batchCoursewareTemplate
                Map<Integer, BatchCoursewareTemplate> templateMap = this.mapBatchCoursewareTemplate(courseRound.getFitnessGroup());
                
                for(int i=courseRound.getFitnessGroupWeek(); i<week; i++) {
                    Date begin = null;
                    Date end = null;
                    if(effectiveStart != null) {
                        begin = DateUtils.addWeeks(effectiveStart, i);
                        end = DateUtils.addDays(begin, 6);
                    }
                    if(end != null && effectiveEnd != null && end.after(effectiveEnd)) {
                        end = effectiveEnd;
                    }
                    
                    BatchCourseware batchCourseware = new BatchCourseware(courseRound, i + 1, begin, end);
                    genericDaoSupport.save(batchCourseware);
                    
                    // create dietary
                    this.createDietary(templateMap.get(i%6), batchCourseware);
                }
            } else if(week < courseRound.getFitnessGroupWeek()) {
                int plannedWeekly = coursewareDao.countBatchCourseware(courseRound, BatchCoursewareType.WEEKLY, week, CoursewareEditStatus.GENERATED);
                if(plannedWeekly >= week) {
                    genericDaoSupport.update(courseRound.editPlannedWeekly(plannedWeekly).finishCourseware());
                } else if(plannedWeekly != courseRound.getPlannedWeekly()) {
                    genericDaoSupport.update(courseRound.editPlannedWeekly(plannedWeekly));
                }
                
                //disable batchCourseware which's sorting is bigger than week
                this.disableBatchCourseware(courseRound, week);
            }
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#getStudentTags(co.fitstart.entity.user.Student)
     */
    public Map<String, Object> getStudentTags(Student student) {
        Map<String, Object> map = new HashMap<>();
        
        List<Long> tagIds = student.getArrayTagIds();
        if(!tagIds.isEmpty()) {
            List<CoursewareTag> tags = this.listCoursewareTags(student.getArrayTagIds());
            
            if(!tags.isEmpty()) {
                StringBuffer studentTags = new StringBuffer();
                List<Long> fitnessTagIds = new ArrayList<>();
                
                for(CoursewareTag tag : tags) {
                    studentTags.append(tag.getName() + ",");
                    fitnessTagIds.add(tag.getFitnessTag().getId());
                }
                
                map.put("tags", studentTags.substring(0, studentTags.length() - 1));
                map.put("tagIds", fitnessTagIds);
            }
        }
        
        return map;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#listDietaryTemplate(co.fitstart.entity.courseware.dietary.DietaryProgramType)
     */
    public List<DietaryProgramTemplate> listDietaryTemplate(DietaryProgramType type) {
        return coursewareTemplateDao.listDietaryTemplate(type);
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#editDietary(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware, co.fitstart.weapp.api.entity.DietaryRequest)
     */
    public void editDietary(BatchCourseware batchCourseware, DietaryRequest dietary) {
        DietaryProgramTemplate breakfastTemplate = null;
        DietaryProgramTemplate breakfastSnackTemplate = null;
        DietaryProgramTemplate lunchTemplate = null;
        DietaryProgramTemplate lunchSnackTemplate = null;
        DietaryProgramTemplate dinnerTemplate = null;
        DietaryProgramTemplate dinnerSnackTemplate = null;
        
        if(dietary.getBreakfastId() != null) {
            breakfastTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dietary.getBreakfastId());
        }
        if(dietary.getBreakfastSnackId() != null) {
            breakfastSnackTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dietary.getBreakfastSnackId());
        }
        if(dietary.getLunchId() != null) {
            lunchTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dietary.getLunchId());
        }
        if(dietary.getLunchSnackId() != null) {
            lunchSnackTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dietary.getLunchSnackId());
        }
        if(dietary.getDinnerId() != null) {
            dinnerTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dietary.getDinnerId());
        }
        if(dietary.getDinnerSnackId() != null) {
            dinnerSnackTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dietary.getDinnerSnackId());
        }
        
        Dietary originalDietary = this.loadDietary(batchCourseware);
        
        if(originalDietary == null) {
            originalDietary = new Dietary(batchCourseware);
            genericDaoSupport.save(originalDietary.edit(breakfastTemplate, dietary.getBreakfastDescription(), breakfastSnackTemplate, dietary.getBreakfastSnackDescription(), lunchTemplate, dietary.getLunchDescription(), lunchSnackTemplate, dietary.getLunchSnackDescription(), dinnerTemplate, dietary.getDinnerDescription(), dinnerSnackTemplate, dietary.getDinnerSnackDescription(), dietary.getWaterIntake(), dietary.getRemark()));
        } else {
            genericDaoSupport.update(originalDietary.edit(breakfastTemplate, dietary.getBreakfastDescription(), breakfastSnackTemplate, dietary.getBreakfastSnackDescription(), lunchTemplate, dietary.getLunchDescription(), lunchSnackTemplate, dietary.getLunchSnackDescription(), dinnerTemplate, dietary.getDinnerDescription(), dinnerSnackTemplate, dietary.getDinnerSnackDescription(), dietary.getWaterIntake(), dietary.getRemark()));
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#editExercise(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware, co.fitstart.weapp.api.entity.ExerciseRequest)
     */
    public void editExercise(BatchCourseware batchCourseware, ExerciseRequest exercise) {
        Exercise originalExercise = this.loadExercise(batchCourseware);
        
        if(originalExercise == null) {
            originalExercise = new Exercise();
            genericDaoSupport.save(originalExercise.initialize(batchCourseware).edit(
                    exercise.getHomeworkFrequency(), exercise.getLissTypes(),
                    exercise.getOtherLiss(), exercise.getHit(),
                    exercise.getCore(), exercise.getRemark()));
        } else {
            genericDaoSupport.update(originalExercise.edit(
                    exercise.getHomeworkFrequency(), exercise.getLissTypes(),
                    exercise.getOtherLiss(), exercise.getHit(),
                    exercise.getCore(), exercise.getRemark()));
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#send(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware)
     */
    public void send(BatchCourseware batchCourseware) {
        Dietary dietary = this.loadDietary(batchCourseware);
        
        if(dietary == null || StringUtils.isBlank(dietary.getBreakfastDescription())
                || StringUtils.isBlank(dietary.getLunchDescription()) || StringUtils.isBlank(dietary.getDinnerDescription())) {
            throw new BusinessException("早午餐为空,不能发送");
        } else {
            Exercise exercise = this.loadExercise(batchCourseware);
            
            if(exercise == null || exercise.getHomeworkFrequency() == null) {
                throw new BusinessException("家庭作业频率保证为空,不能发送");
            } else if(StringUtils.isBlank(exercise.getLiss()) && StringUtils.isBlank(exercise.getHit()) && StringUtils.isBlank(exercise.getCore())) {
                throw new BusinessException("家庭作业LISS、HIIT、核心训练都为空，不能发送");
            } else {
                genericDaoSupport.update(batchCourseware.link("batch_courseware_link", null));
                
                smsSender.sendMessage(batchCourseware.getCourseRound().getStudentMobile(), "【人马君】尊敬的人马君学员,您有新课件，请在个人中心我的课程里查收，如有问题，请联系您的主教练问询，谢谢。希望您在人马君遇见更好的自己。");
                
                CourseRound courseRound = batchCourseware.getCourseRound();
                courseRound.editPlannedAmount("weekly", 1);
                if(courseRound.getPlannedWeekly() == courseRound.getEffectiveWeek()) {
                    courseRound.finishCourseware();
                }
                genericDaoSupport.update(courseRound);
            }
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#generallyListScheduleCourseware(co.fitstart.entity.course.round.CourseRound)
     */
    public List<CourseSchedule> generallyListScheduleCourseware(CourseRound courseRound) {
        List<CourseSchedule> courseSchedules = new ArrayList<>();
        
        courseSchedules.addAll(courseScheduleDao.list(courseRound, Arrays.asList(new ScheduleStatus[]{ScheduleStatus.READY, ScheduleStatus.FINISH, ScheduleStatus.EXPIRY})));
        
        if(courseRound.getCourseType().isIndividual() && !courseSchedules.isEmpty()) {
            List<PlannedSchedule> plannedSchedules = coursewareDao.listPlannedSchedule(courseRound, 0, courseSchedules.size());
            
            if(!plannedSchedules.isEmpty()) {
                int i = 0;
                for(PlannedSchedule plannedSchedule : plannedSchedules) {
                    CourseSchedule courseSchedule = courseSchedules.get(i++);
                    if(courseSchedule.getContent() == null) {
                        genericDaoSupport.update(courseSchedule.editContent(plannedSchedule.getContent()));
                    }
                }
            }
        }
        
        if(courseRound.getCourseType().isIndividual()) {
            courseSchedules.addAll(courseScheduleDao.listAll(courseRound, ScheduleStatus.SCHEDULE));
        }
        
        return courseSchedules;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#initlizeContent(co.fitstart.entity.course.schedule.CourseSchedule)
     */
    public void initlizeContent(CourseSchedule courseSchedule) {
        if(courseSchedule.getContent() == null) {
            PlannedSchedule plannedSchedule = this.getPlannedSchedule(courseSchedule.getCourseRound(), this.getSession(courseSchedule));
            if(plannedSchedule != null) {
                genericDaoSupport.update(courseSchedule.editContent(plannedSchedule.getContent()));
            }
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#getContent(co.fitstart.entity.course.schedule.CourseSchedule)
     */
    public List<Map<String, Object>> getContent(CourseSchedule courseSchedule) {
        List<Map<String, Object>> result = new ArrayList<>();
        
        List<ExerciseContent> contents = courseSchedule.getContentList();
        for(ExerciseContent content : ExerciseContent.values()) {
            Map<String, Object> map = new HashMap<>();
            
            map.put("name", content.getDescription());
            map.put("value", content);
            map.put("selected", contents.contains(content));
            
            result.add(map);
        }
        
        return result;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#editContent(co.fitstart.entity.course.schedule.CourseSchedule, java.util.List)
     */
    public void editContent(CourseSchedule courseSchedule, List<ExerciseContent> contents) {
        genericDaoSupport.update(courseSchedule.editContent(contents));
        
        this.recalculatePlannedContent(courseSchedule.getCourseRound());
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#addCourseware(co.fitstart.entity.course.schedule.CourseSchedule, java.util.List)
     */
    public void addCourseware(CourseSchedule courseSchedule, List<Long> movementIds) {
        if(movementIds != null && movementIds.size() > 0) {
            for(Long movementId : movementIds) {
                Movement movement = genericDaoSupport.load(Movement.class, movementId);
                if(!movement.isDisabled()) {
                    Courseware courseware = new Courseware(courseSchedule, movement);
                    genericDaoSupport.save(courseware);
                }
            }
        }
        
        this.recalculatePlannedCourseware(courseSchedule.getCourseRound());
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#addCourseware(co.fitstart.entity.course.schedule.CourseSchedule, co.fitstart.entity.courseware.fitnessgroup.FitnessGroup)
     */
    public void addCourseware(CourseSchedule courseSchedule, FitnessGroup fitnessGroup) {
        SpecialCoursewareTemplate specialCoursewareTemplate = coursewareTemplateDao.loadSpecialCoursewareTemplate(fitnessGroup);
        
        if(specialCoursewareTemplate != null) {
            List<CoursewareTemplate> coursewareTemplates = coursewareTemplateDao.listCoursewareTemplate(specialCoursewareTemplate);
            
            // create courseware template
            for(CoursewareTemplate template : coursewareTemplates) {
                Courseware courseware = new Courseware(courseSchedule, template);
                genericDaoSupport.save(courseware);
            }
        }
        
        this.recalculatePlannedCourseware(courseSchedule.getCourseRound());
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#deleteCourseware(co.fitstart.entity.course.schedule.CourseSchedule, java.util.List)
     */
    public void deleteCourseware(CourseSchedule courseSchedule, List<Long> coursewareIds) {
        if(coursewareIds != null && coursewareIds.size() > 0) {
            coursewareDao.disable(courseSchedule, coursewareIds);
        }
        
        this.recalculatePlannedCourseware(courseSchedule.getCourseRound());
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#editCourseware(co.fitstart.entity.course.schedule.CourseSchedule, java.util.List)
     */
    public void editCourseware(CourseSchedule courseSchedule, List<CoursewareRequest> coursewares) {
        if(coursewares != null && coursewares.size() > 0) {
            for(CoursewareRequest courseware : coursewares) {
                Courseware originalCourseware = genericDaoSupport.load(Courseware.class, courseware.getId());
                
                if(originalCourseware.getCourseSchedule() == courseSchedule) {
                    genericDaoSupport.update(originalCourseware.edit(courseware.getGroupAmount(), courseware.getMovementNumber(), StringUtils.isNotBlank(courseware.getMaxWeight()) ? courseware.getMaxWeight().trim() : null, StringUtils.isNotBlank(courseware.getTime()) ? courseware.getTime().trim() : null));
                }
            }
        }
    }

    /**
     * 
     * @param fitnessGroup
     * @return
     */
    private Map<Integer, BatchCoursewareTemplate> mapBatchCoursewareTemplate(FitnessGroup fitnessGroup) {
        Map<Integer, BatchCoursewareTemplate> templateMap = new HashMap<>();
        List<BatchCoursewareTemplate> batchCoursewareTemplates = coursewareTemplateDao.listBatchCoursewareTemplate(fitnessGroup, BatchCoursewareType.WEEKLY, 6);
        
        for(BatchCoursewareTemplate template : batchCoursewareTemplates) {
            templateMap.put(template.getSorting() - 1, template);
        }
        
        return templateMap;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.weapp.api.service.CoursewareService#getWeekBeginAndEnd(co.fitstart.entity.course.round.CourseRound, int)
     */
    public Map<String, Date> getWeekBeginAndEnd(CourseRound courseRound, int week) {
        Map<String, Date> result = new HashMap<>();
        
        if(courseRound.getEffectiveStart() != null) {
            result.put("begin", DateUtils.addWeeks(courseRound.getEffectiveStart(), week - 1));
            result.put("end", DateUtils.addDays(courseRound.getEffectiveStart(), week * 7 - 1));
        }
        
        return result;
    }

    /**
     * 
     * @param courseRound
     * @param week
     */
    private void disableBatchCourseware(CourseRound courseRound, int week) {
        coursewareDao.disableBatchCourseware(courseRound, week);
        
        List<Dietary> dietarys = coursewareDao.listDieatry(courseRound, week);
        for(Dietary dietary : dietarys) {
            genericDaoSupport.update(dietary.disable());
        }
        
        List<Exercise> exercises = coursewareDao.listExercise(courseRound, week);
        for(Exercise exercise : exercises) {
            genericDaoSupport.update(exercise.disable());
        }
    }

    /**
     * 重新计算训练部位完成情况
     * @param courseRound
     */
    private void recalculatePlannedContent(CourseRound courseRound) {
        if(courseRound.getCourseType().isYear() || (courseRound.getCourseType().isIndividual() && !courseRound.getCourse().isExperience())) {
            if(courseRound.getCourseStatus().isReady() || courseRound.getCourseStatus().isHold()
                    || courseRound.getCourseStatus().isExpire() || courseRound.getCourseStatus().isActivate()) {
                CourseSchedule courseSchedule = courseScheduleDao.load(courseRound);
                if(courseSchedule == null) {
                    genericDaoSupport.update(courseRound.editPlannedContent(0));
                } else {
                    genericDaoSupport.update(courseRound.editPlannedContent(courseScheduleDao.count(courseRound, courseSchedule.getOccurDate(), courseSchedule.getOccurHour())));
                }
            }
        }
    }

    /**
     * 重新计算上课课件完成情况
     * @param courseRound
     */
    private void recalculatePlannedCourseware(CourseRound courseRound) {
        if(courseRound.getCourseType().isYear() || (courseRound.getCourseType().isIndividual() && !courseRound.getCourse().isExperience())) {
            if(courseRound.getCourseStatus().isReady() || courseRound.getCourseStatus().isHold()
                    || courseRound.getCourseStatus().isExpire() || courseRound.getCourseStatus().isActivate()) {
                CourseSchedule courseSchedule = coursewareDao.loadCourseSchedue(courseRound);
                if(courseSchedule == null) {
                    genericDaoSupport.update(courseRound.editPlannedCourseware(0));
                } else {
                    genericDaoSupport.update(courseRound.editPlannedCourseware(courseScheduleDao.count(courseRound, courseSchedule.getOccurDate(), courseSchedule.getOccurHour())));
                }
            }
        }
    }
}
